<?php
namespace Bundle\FireIceBundle;

use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;

class DynamicRouter
{
    /**
     * @var \Symfony\Component\Routing\RouteCollection
     */
    private $_routeCollection;

    //@TODO dirty!
    private $_loaded = true;

    private $_fireice;

    public function __construct(FireIceEngine $engine)
    {
        $this->_fireice = $engine;

        $this->_routeCollection = new RouteCollection();
    }

    public function getRouteCollection()
    {
        if(!$this->_loaded)
        {
            $this->load('');
        }
        return $this->_routeCollection;
    }

    /**
     * @TODO ZOMG HOW it's bad
     * @return void
     */
    public function load($fn)
    {
        if(file_exists($fn))
        {
            $this->_routeCollection = unserialize(file_get_contents($fn));
        }
    }

    public function nodeUpdated($event)
    {

        //var_dump($event->getParameters());
    }

    /**
     * Проверяет $node нод дерева на наличае всех необходимых значений
     * @throws Exception
     * @param  $node
     * @return void
     */
    public static function checkNodeValues($node)
    {
        // @TODO cache?

        if(null == $node)
        {
            throw new \Exception('Node can\'t be null.');
        }

        if(null == $node->getController())
        {
            throw new \Exception(sprintf('Node `%d` has no controller specified', $node->getId()));
        }

        if(null == $node->getDescription())
        {
            throw new \Exception(sprintf('Node `%d` has no description.', $node->getId()));
        }

        if(null == $node->getDescription()->getModule())
        {
            throw new \Exception(sprintf('Node `%d` has no module specified.', $node->getId()));
        }
    }

    /**
     * @param  $node В данном случае объект типа Tree
     * @remarks Нет проверок на корректность путей
     * @return void Если $return = true создаёт новую коллекцию путей в которую вносится роутинг модуля
     * иначе всё добавление идёт в общую коллекцию
     */
    public function buildRoutingForNode($node, $return = false)
    {
        // проверка значений нода
        self::checkNodeValues($node);

        // назначение
        $r = $return ? new RouteCollection() : $this->_routeCollection;

        // базовый путь, берётся от корня
        $basepath = '/';

        // проход всех родителей нода, для составления полного пути

        $parent = $node;
        while(null != ($parent = $parent->getParent()))
        {
            // проверка родительского нода на заполненность параметров
            self::checkNodeValues($parent);

            // объединение путей
            $basepath = '/' . $parent->getPath() . $basepath;
        }

        // получение полного пути нода
        $basepath = $basepath . $node->getPath();

        // получение внутренней маршрутизации модуля
        $moduleRouting = $this->_fireice->modules[$node->getDescription()->getModule()]->getRouting();

        // разбор маршрутизации
        foreach($moduleRouting as $action => $rules)
        {
            $pattern = '';
            if(is_array($rules) && array_key_exists('pattern', $rules))
            {
                $pattern = $rules['pattern'];
            }
            else if (is_string($rules))
            {
                $pattern = $rules;
            }
            else
            {
                throw new \Exception(sprintf('Action `%s` of module `%s` has incorrect routing definition.',
                $action, $node->getDescription()->getModule()));
            }
            
            // получение параметров из ссылки

            $result = \preg_match_all('%(:[^:/]*)%', $pattern, $vars, PREG_PATTERN_ORDER);

            $variables = array();

            if(0 !== $result)
            {
                foreach($vars[0] as $variable)
                {
                    $variables[] = \substr($variable, 1);
                }
            }

            $targetMethod = $this->_fireice->getControllerBuilder()->
                    getTargetMethod($node, $action, $variables);

            //@TODO доделать таки полный парсинг массива, полученного из модуля
            $route = new Route($basepath . $pattern, array('_controller' => $targetMethod));

            $r->addRoute($node->getId() . '_' . $action, $route);
        }

        $this->_loaded = true;

        return $r;
    }

}
